package org.martyn.danbooru.http;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.PlainSocketFactory;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.scheme.SocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.martyn.danbodroid.logger.Logger;
import org.martyn.danbooru.error.DanbooruException;
import org.martyn.danbooru.utils.URLUtils;

public class HttpApi
{

	private static final String CLIENT_VERSION_HEADER = "User-Agent";
	private static final int TIMEOUT = 60;

	private final DefaultHttpClient httpClient;
	private final String clientVersion;

	public HttpApi(DefaultHttpClient httpClient, String clientVersion)
	{

		this.httpClient = httpClient;
		this.clientVersion = clientVersion;
	}

	public InputStream executeHttpRequest(HttpRequestBase httpRequest)
			throws DanbooruException, IOException
	{
		Logger.Verbose("", "executing HttpRequest for: "
				+ httpRequest.getURI().toString());

		try
		{
			httpClient.getConnectionManager().closeExpiredConnections();
			HttpResponse response = httpClient.execute(httpRequest);

			int statusCode = response.getStatusLine().getStatusCode();
			switch (statusCode)
			{
			case 200:
			{
				InputStream inputStream = response.getEntity().getContent();

				return inputStream;
			}
			case 404:
			{
				response.getEntity().consumeContent();
				throw new DanbooruException(response.getStatusLine().toString());
			}
			case 500:
			{
				response.getEntity().consumeContent();
				Logger.Verbose("", "HTTP Code: 500");
				throw new DanbooruException(
						"Danbooru has internal server error. Try again later.");
			}
			case 503:
			{
				response.getEntity().consumeContent();
				Logger.Verbose("", "HTTP Code: 503");
				throw new DanbooruException(
						"Danbooru unavailable. Try again later.");
			}
			default:
			{
				response.getEntity().consumeContent();
				throw new DanbooruException("Error connecting to Danbooru: "
						+ statusCode + ". Try again later.");
			}
			}
		}
		catch (IOException ex)
		{
			httpRequest.abort();
			throw ex;
		}

	}

	public HttpGet createHttpGet(String url, NameValuePair... nameValuePairs)
	{

		Logger.Verbose("", "creating HttpGet for: " + url);

		HttpGet httpGet;

		String query = null;

		if (nameValuePairs != null)
		{
			query = URLUtils.buildQuery(stripNulls(nameValuePairs));
		}

		if (query == "" || query == null)
		{
			httpGet = new HttpGet(url);
		}
		else
		{
			httpGet = new HttpGet(url + "?" + query);
		}

		httpGet.addHeader(CLIENT_VERSION_HEADER, clientVersion);

		Logger.Verbose("", "Created: " + httpGet.getURI());

		return httpGet;
	}

	/*
	 * public HttpPost createHttpPost(String url, NameValuePair...
	 * nameValuePairs) {
	 * 
	 * Logger.Verbose("", "creating HttpPost for: " + url);
	 * 
	 * HttpPost httpPost = new HttpPost(url);
	 * httpPost.addHeader(CLIENT_VERSION_HEADER, clientVersion);
	 * 
	 * try { httpPost.setEntity(entity)ntity(new
	 * URLUtils.(stripNulls(nameValuePairs)); } catch
	 * (UnsupportedEncodingException ex) {
	 * 
	 * throw new IllegalArgumentException("Unable to encode http parameters.");
	 * }
	 * 
	 * Logger.Verbose("", "Created: " + httpPost);
	 * 
	 * return httpPost; }
	 */

	/**
	 * Clears a nameValuePair List from null items
	 * 
	 * @param nameValuePairs
	 * @return
	 */
	private List<NameValuePair> stripNulls(NameValuePair... nameValuePairs)
	{
		List<NameValuePair> params = new ArrayList<NameValuePair>();

		for (int i = 0; i < nameValuePairs.length; i++)
		{
			NameValuePair param = nameValuePairs[i];

			if (param.getValue() != null)
			{

				Logger.Verbose("", "Param: " + param);

				params.add(param);
			}
		}

		return params;
	}

	/**
	 * Create a thread-safe client. This client does not do redirecting, to
	 * allow us to capture correct "error" codes.
	 * 
	 * @return HttpClient
	 */
	public static final DefaultHttpClient createHttpClient()
	{

		// Sets up the http part of the service.
		final SchemeRegistry supportedSchemes = new SchemeRegistry();

		// Register the "http" protocol scheme, it is required by the default
		// operator to look up socket factories.
		final SocketFactory sf = PlainSocketFactory.getSocketFactory();

		supportedSchemes.register(new Scheme("http", sf, 80));

		// Set some client http client parameter defaults.
		final HttpParams httpParams = createHttpParams();
		HttpClientParams.setRedirecting(httpParams, false);

		final ClientConnectionManager ccm = new ThreadSafeClientConnManager(
				httpParams, supportedSchemes);

		return new DefaultHttpClient(ccm, httpParams);
	}

	/**
	 * Create the default HTTP protocol parameters.
	 */
	private static final HttpParams createHttpParams()
	{
		final HttpParams params = new BasicHttpParams();

		// Turn off stale checking. Our connections break all the time anyway,
		// and it's not worth it to pay the penalty of checking every time.
		HttpConnectionParams.setStaleCheckingEnabled(params, false);

		HttpConnectionParams.setConnectionTimeout(params, TIMEOUT * 1000);
		HttpConnectionParams.setSoTimeout(params, TIMEOUT * 1000);
		HttpConnectionParams.setSocketBufferSize(params, 8192);

		return params;
	}

}
